repo: Add a "force copy" flag to checkout
authorColin Walters <walters@verbum.org>
Fri, 21 Apr 2017 19:43:17 +0000 (15:43 -0400)
committerAtomic Bot <atomic-devel@projectatomic.io>
Mon, 24 Apr 2017 15:26:11 +0000 (15:26 +0000)
This is intended to be used for copying `/usr/etc` → `/etc` for
deployments.

A TODO here is to use `glnx_file_copy_at()` if the repo mode allows
it - then we'd use reflinks if available.

Closes: #804
Approved by: jlebon

src/libostree/ostree-repo-checkout.c
src/libostree/ostree-repo.h
src/ostree/ot-builtin-checkout.c
tests/basic-test.sh

index f48aff9e2f2925c4485d9019b2729f8e9126124f..5b87c7eca0d229a0f484dbd698b0f17591fa8792 100644 (file)
@@ -403,7 +403,7 @@ checkout_one_file_at (OstreeRepo                        *repo,
 
       need_copy = FALSE;
     }
-  else
+  else if (!options->force_copy)
     {
       HardlinkResult hardlink_res = HARDLINK_RESULT_NOT_SUPPORTED;
       /* Try to do a hardlink first, if it's a regular file.  This also
@@ -895,6 +895,8 @@ ostree_repo_checkout_at (OstreeRepo                        *self,
   if (ostree_repo_get_mode (self) == OSTREE_REPO_MODE_BARE_USER_ONLY)
     options->mode = OSTREE_REPO_CHECKOUT_MODE_USER;
 
+  g_return_val_if_fail (!(options->force_copy && options->no_copy_fallback), FALSE);
+
   g_autoptr(GFile) commit_root = (GFile*) _ostree_repo_file_new_for_commit (self, commit, error);
   if (!commit_root)
     return FALSE;
index 1664d65df01843921d0892a28053d15e9a7cb957..2e34c21c59199ca124929ce61cb05269d6d67315 100644 (file)
@@ -767,7 +767,8 @@ typedef struct {
   gboolean enable_fsync;  /* Deprecated */
   gboolean process_whiteouts;
   gboolean no_copy_fallback;
-  gboolean unused_bools[7];
+  gboolean force_copy; /* Since: 2017.6 */
+  gboolean unused_bools[6];
 
   const char *subpath;
 
index 74e27cfb600b03e4e678abc1fa05ecf945f39610..9ba804f899c99b69a6f0c0d465e3949a6466a733 100644 (file)
@@ -42,6 +42,7 @@ static gboolean opt_from_stdin;
 static char *opt_from_file;
 static gboolean opt_disable_fsync;
 static gboolean opt_require_hardlinks;
+static gboolean opt_force_copy;
 
 static gboolean
 parse_fsync_cb (const char  *option_name,
@@ -71,6 +72,7 @@ static GOptionEntry options[] = {
   { "from-file", 0, 0, G_OPTION_ARG_STRING, &opt_from_file, "Process many checkouts from input file", "FILE" },
   { "fsync", 0, 0, G_OPTION_ARG_CALLBACK, parse_fsync_cb, "Specify how to invoke fsync()", "POLICY" },
   { "require-hardlinks", 'H', 0, G_OPTION_ARG_NONE, &opt_require_hardlinks, "Do not fall back to full copies if hardlinking fails", NULL },
+  { "force-copy", 'C', 0, G_OPTION_ARG_NONE, &opt_force_copy, "Never hardlink (but may reflink if available)", NULL },
   { NULL }
 };
 
@@ -89,7 +91,7 @@ process_one_checkout (OstreeRepo           *repo,
    * `ostree_repo_checkout_at` until such time as we have a more
    * convenient infrastructure for testing C APIs with data.
    */
-  if (opt_disable_cache || opt_whiteouts || opt_require_hardlinks || opt_union_add)
+  if (opt_disable_cache || opt_whiteouts || opt_require_hardlinks || opt_union_add || opt_force_copy)
     {
       OstreeRepoCheckoutAtOptions options = { 0, };
 
@@ -102,6 +104,11 @@ process_one_checkout (OstreeRepo           *repo,
                        "Cannot specify both --union and --union-add");
           goto out;
         }
+      if (opt_require_hardlinks && opt_force_copy)
+        {
+          glnx_throw (error, "Cannot specify both --require-hardlinks and --force-copy");
+          goto out;
+        }
       else if (opt_union)
         options.overwrite_mode = OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES;
       else if (opt_union_add)
@@ -111,6 +118,7 @@ process_one_checkout (OstreeRepo           *repo,
       if (subpath)
         options.subpath = subpath;
       options.no_copy_fallback = opt_require_hardlinks;
+      options.force_copy = opt_force_copy;
 
       if (!ostree_repo_checkout_at (repo, &options,
                                     AT_FDCWD, destination,
index f4b2b118ed07ce86acb26e9e474d79ec98a23d31..6ddf7b2e544f8f0fecb2f3d394e4157b7cd9f83f 100644 (file)
@@ -19,7 +19,7 @@
 
 set -euo pipefail
 
-echo "1..65"
+echo "1..66"
 
 $CMD_PREFIX ostree --version > version.yaml
 python -c 'import yaml; yaml.safe_load(open("version.yaml"))'
@@ -28,7 +28,7 @@ echo "ok yaml version"
 CHECKOUT_U_ARG=""
 COMMIT_ARGS=""
 DIFF_ARGS=""
-if grep bare-user-only repo/config; then
+if grep -q bare-user-only repo/config; then
     # In bare-user-only repos we can only represent files with uid/gid 0, no
     # xattrs and canonical permissions, so we need to commit them as such, or
     # we end up with repos that don't pass fsck
@@ -50,11 +50,14 @@ validate_checkout_basic() {
 
 $OSTREE checkout test2 checkout-test2
 validate_checkout_basic checkout-test2
+if grep -q 'mode=bare$' repo/config; then
+    assert_not_streq $(stat -c '%h' checkout-test2/firstfile) 1
+fi
 echo "ok checkout"
 
 # Note this tests bare-user *and* bare-user-only
 rm checkout-test2 -rf
-if grep bare-user repo/config; then
+if grep -q bare-user repo/config; then
     $OSTREE checkout -U -H test2 checkout-test2
 else
     $OSTREE checkout -H test2 checkout-test2
@@ -78,6 +81,14 @@ fi
 fi
 echo "ok checkout -H"
 
+rm checkout-test2 -rf
+$OSTREE checkout -C test2 checkout-test2
+for file in firstfile baz/cow baz/alink; do
+    assert_streq $(stat -c '%h' checkout-test2/$file) 1
+done
+
+echo "ok checkout -C"
+
 $OSTREE rev-parse test2
 $OSTREE rev-parse 'test2^'
 $OSTREE rev-parse 'test2^^' 2>/dev/null && fatal "rev-parse test2^^ unexpectedly succeeded!"